/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is ART Ontology API.
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2009.
 * All Rights Reserved.
 *
 * The ART Ontology API were developed by the Artificial Intelligence Research Group
 * (art.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about the ART Ontology API can be obtained at 
 * http//art.uniroma2.it/owlart
 *
 */

/*
 * Contributor(s): Armando Stellato stellato@info.uniroma2.it
 */
package it.uniroma2.art.owlart.models.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.Map;

import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.exceptions.UnavailableResourceException;
import it.uniroma2.art.owlart.exceptions.UnsupportedQueryLanguageException;
import it.uniroma2.art.owlart.exceptions.UnsupportedRDFFormatException;
import it.uniroma2.art.owlart.io.RDFFormat;
import it.uniroma2.art.owlart.model.ARTBNode;
import it.uniroma2.art.owlart.model.ARTLiteral;
import it.uniroma2.art.owlart.model.ARTNode;
import it.uniroma2.art.owlart.model.ARTResource;
import it.uniroma2.art.owlart.model.ARTStatement;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.models.BaseRDFTripleModel;
import it.uniroma2.art.owlart.models.RDFModel;
import it.uniroma2.art.owlart.navigation.ARTNamespaceIterator;
import it.uniroma2.art.owlart.navigation.ARTNodeIterator;
import it.uniroma2.art.owlart.navigation.ARTResourceIterator;
import it.uniroma2.art.owlart.navigation.ARTStatementIterator;
import it.uniroma2.art.owlart.navigation.ARTURIResourceIterator;
import it.uniroma2.art.owlart.query.BooleanQuery;
import it.uniroma2.art.owlart.query.GraphQuery;
import it.uniroma2.art.owlart.query.MalformedQueryException;
import it.uniroma2.art.owlart.query.Query;
import it.uniroma2.art.owlart.query.QueryLanguage;
import it.uniroma2.art.owlart.query.TupleQuery;
import it.uniroma2.art.owlart.vocabulary.RDF;
import it.uniroma2.art.owlart.vocabulary.RDFS;

public class RDFModelImpl extends BaseRDFModelImpl implements RDFModel {

	protected BaseRDFTripleModel baseRep;
	
	public RDFModelImpl(BaseRDFTripleModel baseRep) {
		this.baseRep = baseRep;
	}

	/****************************
	 * /* ADD/REMOVE METHODS ***
	 ****************************/

	public void addInstance(String uri, ARTResource cls, ARTResource... graphs) throws ModelUpdateException {
		baseRep.addTriple(baseRep.createURIResource(uri), RDF.Res.TYPE, cls, graphs);
	}

	public void addProperty(String propertyURI, ARTURIResource superProperty, ARTResource... graphs)
			throws ModelUpdateException {
		ARTURIResource newProp = baseRep.createURIResource(propertyURI);
		baseRep.addTriple(newProp, RDF.Res.TYPE, RDF.Res.PROPERTY, graphs);
		if (superProperty != null)
			baseRep.addTriple(newProp, RDFS.Res.SUBPROPERTYOF, superProperty, graphs);
	}

	public void addType(ARTResource ind, ARTResource cls, ARTResource... graphs) throws ModelUpdateException {
		baseRep.addTriple(ind, RDF.Res.TYPE, cls, graphs);
	}

	public void removeType(ARTResource ind, ARTResource cls, ARTResource... graphs)
			throws ModelUpdateException {
		baseRep.deleteTriple(ind, RDF.Res.TYPE, cls, graphs);
	}

	public void instantiatePropertyWithPlainLiteral(ARTResource subject, ARTURIResource predicate,
			String value, ARTResource... graphs) throws ModelUpdateException {
		ARTLiteral lit = baseRep.createLiteral(value);
		baseRep.addTriple(subject, predicate, lit, graphs);
	}

	public void instantiatePropertyWithPlainLiteral(ARTResource subject, ARTURIResource predicate,
			String value, String lang, ARTResource... graphs) throws ModelUpdateException {
		ARTLiteral lit = baseRep.createLiteral(value, lang);
		baseRep.addTriple(subject, predicate, lit, graphs);
	}

	public void instantiatePropertyWithTypedLiteral(ARTResource subject, ARTURIResource property,
			String value, ARTURIResource datatype, ARTResource... graphs) throws ModelUpdateException {
		ARTLiteral lit = baseRep.createLiteral(value, datatype);
		baseRep.addTriple(subject, property, lit, graphs);
	}

	public void instantiatePropertyWithResource(ARTResource subject, ARTURIResource predicate,
			ARTResource object, ARTResource... graphs) throws ModelUpdateException {
		baseRep.addTriple(subject, predicate, object, graphs);
	}

	/*************************
	 * /* BOOLEAN METHODS ***
	 *************************/

	public boolean hasType(ARTResource ind, ARTResource type, boolean inferred, ARTResource... graphs)
			throws ModelAccessException {
		return baseRep.hasTriple(ind, RDF.Res.TYPE, type, inferred, graphs);
	}

	public boolean isProperty(ARTResource prop, ARTResource... graphs) throws ModelAccessException {
		return baseRep.hasTriple(prop, RDF.Res.TYPE, RDF.Res.PROPERTY, true, graphs);
	}

	/*************************
	 * /* RETRIEVE METHODS ***
	 * 
	 * @throws ModelAccessException
	 * @throws UnavailableResourceException
	 *************************/


	public ARTURIResource retrieveURIResource(String uri, ARTResource... graphs) throws ModelAccessException {
		ARTURIResource toBeFound = baseRep.createURIResource(uri);

		if (hasTriple(toBeFound, NodeFilters.ANY, NodeFilters.ANY, true))
			return toBeFound;
		if (hasTriple(NodeFilters.ANY, NodeFilters.ANY, toBeFound, true))
			return toBeFound;
		if (hasTriple(NodeFilters.ANY, toBeFound, NodeFilters.ANY, true))
			return toBeFound;

		return null;
	}
	
	public ARTBNode retrieveBNode(String ID, ARTResource... graphs) throws ModelAccessException {
		ARTBNode toBeFound = baseRep.createBNode(ID);

		if (hasTriple(toBeFound, NodeFilters.ANY, NodeFilters.ANY, true))
			return toBeFound;
		if (hasTriple(NodeFilters.ANY, NodeFilters.ANY, toBeFound, true))
			return toBeFound;

		return null;
	}

	/*************************
	 * /* LIST/GET METHODS ***
	 *************************/

	public ARTURIResourceIterator listProperties(ARTResource... graphs) throws ModelAccessException {
		return new URIResourceIteratorWrappingResourceIterator(new SubjectsOfStatementsIterator(baseRep
				.listStatements(NodeFilters.ANY, RDF.Res.TYPE, RDF.Res.PROPERTY, true, graphs)));
	}

	public ARTResourceIterator listTypes(ARTResource res, boolean inferred, ARTResource... graphs)
			throws ModelAccessException {
		return new ResourceIteratorWrappingNodeIterator(new ObjectsOfStatementsIterator(baseRep
				.listStatements(res, RDF.Res.TYPE, NodeFilters.ANY, inferred, graphs)));
	}

	public ARTResourceIterator listInstances(ARTResource res, boolean inferred, ARTResource... graphs)
			throws ModelAccessException {
		return new SubjectsOfStatementsIterator(baseRep.listStatements(NodeFilters.ANY, RDF.Res.TYPE, res,
				inferred, graphs));
	}

	public ARTURIResourceIterator listNamedInstances(ARTResource... graphs) throws ModelAccessException {
		ARTResourceIterator resIt = new SubjectsOfStatementsIterator(baseRep.listStatements(NodeFilters.ANY,
				RDF.Res.TYPE, RDFS.Res.RESOURCE, true, graphs));
		return new NamedInstancesIteratorFilteringResourceIterator(resIt, this);
	}

	/*************************
	 * /* RENAME METHODS ***
	 * 
	 * @throws ModelUpdateException
	 * @throws ModelAccessException
	 *************************/

	public void renameIndividual(ARTURIResource oldIndividual, String newURI) throws ModelUpdateException {
		ARTURIResource newIndividual = baseRep.createURIResource(newURI);
		ARTStatementIterator statIt;
		try {
			statIt = baseRep.listStatements(oldIndividual, NodeFilters.ANY, NodeFilters.ANY, false);
			while (statIt.streamOpen()) {
				ARTStatement st = statIt.next();
				System.err.println("adding quadruple: " + newIndividual + ":" + st.getPredicate() + ":"
						+ st.getObject() + ":" + st.getNamedGraph());
				addTriple(newIndividual, st.getPredicate(), st.getObject(), st.getNamedGraph());
			}
			statIt.close();

			deleteTriple(oldIndividual, NodeFilters.ANY, NodeFilters.ANY);

			statIt = baseRep.listStatements(NodeFilters.ANY, NodeFilters.ANY, oldIndividual, false);
			while (statIt.streamOpen()) {
				ARTStatement st = statIt.next();
				System.err.println("adding quadruple: " + st.getSubject() + ":" + st.getPredicate() + ":"
						+ newIndividual + ":" + st.getNamedGraph());
				addTriple(st.getSubject(), st.getPredicate(), newIndividual, st.getNamedGraph());
			}
			statIt.close();

			deleteTriple(NodeFilters.ANY, NodeFilters.ANY, oldIndividual);

		} catch (ModelAccessException e) {
			throw new ModelUpdateException(e);
		}
	}

	public void renameProperty(ARTURIResource oldProperty, String newPropertyURI) throws ModelUpdateException {
		ARTURIResource newProperty = baseRep.createURIResource(newPropertyURI);
		ARTStatementIterator statIt;
		try {

			statIt = baseRep.listStatements(oldProperty, NodeFilters.ANY, NodeFilters.ANY, false);
			while (statIt.streamOpen()) {
				ARTStatement st = statIt.next();
				addTriple(newProperty, st.getPredicate(), st.getObject(), st.getNamedGraph());
			}
			statIt.close();

			deleteTriple(oldProperty, NodeFilters.ANY, NodeFilters.ANY);

			statIt = baseRep.listStatements(NodeFilters.ANY, oldProperty, NodeFilters.ANY, false);
			while (statIt.streamOpen()) {
				ARTStatement st = statIt.next();
				addTriple(st.getSubject(), newProperty, st.getObject(), st.getNamedGraph());
			}

			deleteTriple(NodeFilters.ANY, oldProperty, NodeFilters.ANY);

			statIt = baseRep.listStatements(NodeFilters.ANY, NodeFilters.ANY, oldProperty, false);
			while (statIt.streamOpen()) {
				ARTStatement st = statIt.next();
				addTriple(st.getSubject(), st.getPredicate(), newProperty, st.getNamedGraph());
			}
			statIt.close();

			deleteTriple(NodeFilters.ANY, NodeFilters.ANY, oldProperty);

		} catch (ModelAccessException e) {
			throw new ModelUpdateException(e);
		}
	}

	// EXTENSIONS TO TRIPLE QUERY MODEL

	/*
	 * (non-Javadoc)
	 * 
	 * @see it.uniroma2.art.owlart.models.RDFModel#createBooleanQuery(java.lang.String)
	 */
	public BooleanQuery createBooleanQuery(String query) throws UnsupportedQueryLanguageException,
			ModelAccessException, MalformedQueryException {
		return baseRep.createBooleanQuery(QueryLanguage.SPARQL, query, baseRep.getBaseURI());
	}

	public BooleanQuery createBooleanQuery(String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createBooleanQuery(QueryLanguage.SPARQL, query, baseURI);
	}

	public BooleanQuery createBooleanQuery(QueryLanguage ql, String query)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createBooleanQuery(ql, query, baseRep.getBaseURI());
	}

	public GraphQuery createGraphQuery(String query) throws UnsupportedQueryLanguageException,
			ModelAccessException, MalformedQueryException {
		return baseRep.createGraphQuery(QueryLanguage.SPARQL, query, baseRep.getBaseURI());
	}

	public GraphQuery createGraphQuery(String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createGraphQuery(QueryLanguage.SPARQL, query, baseURI);
	}

	public GraphQuery createGraphQuery(QueryLanguage ql, String query)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createGraphQuery(ql, query, baseRep.getBaseURI());
	}

	public Query createQuery(String query) throws UnsupportedQueryLanguageException, ModelAccessException,
			MalformedQueryException {
		return baseRep.createQuery(QueryLanguage.SPARQL, query, baseRep.getBaseURI());
	}

	public Query createQuery(String query, String baseURI) throws UnsupportedQueryLanguageException,
			ModelAccessException, MalformedQueryException {
		return baseRep.createQuery(QueryLanguage.SPARQL, query, baseURI);
	}

	public Query createQuery(QueryLanguage ql, String query) throws UnsupportedQueryLanguageException,
			ModelAccessException, MalformedQueryException {
		return baseRep.createQuery(ql, query, baseRep.getBaseURI());
	}

	public TupleQuery createTupleQuery(String query) throws UnsupportedQueryLanguageException,
			ModelAccessException, MalformedQueryException {
		return baseRep.createTupleQuery(QueryLanguage.SPARQL, query, baseRep.getBaseURI());
	}

	public TupleQuery createTupleQuery(String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createTupleQuery(QueryLanguage.SPARQL, query, baseURI);
	}

	public TupleQuery createTupleQuery(QueryLanguage ql, String query)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createTupleQuery(ql, query, baseRep.getBaseURI());
	}

	public void addRDF(File inputFile, String baseURI, RDFFormat rdfFormat, ARTResource... graphs)
			throws FileNotFoundException, IOException, ModelAccessException, ModelUpdateException,
			UnsupportedRDFFormatException {
		baseRep.addRDF(inputFile, baseURI, rdfFormat, graphs);
	}

	public void addRDF(URL url, String baseURI, RDFFormat rdfFormat, ARTResource... graphs)
			throws FileNotFoundException, IOException, ModelAccessException, ModelUpdateException,
			UnsupportedRDFFormatException {
		baseRep.addRDF(url, baseURI, rdfFormat, graphs);
	}

	public void addStatement(ARTStatement stat, ARTResource... graphs) throws ModelUpdateException {
		baseRep.addStatement(stat, graphs);
	}

	public void addTriple(ARTResource subject, ARTURIResource predicate, ARTNode object,
			ARTResource... graphs) throws ModelUpdateException {
		baseRep.addTriple(subject, predicate, object, graphs);
	}

	public void clearRDF(ARTResource... graphs) throws ModelUpdateException {
		baseRep.clearRDF(graphs);
	}

	public void close() throws ModelUpdateException {
		baseRep.close();
	}

	public ARTBNode createBNode() {
		return baseRep.createBNode();
	}

	public ARTBNode createBNode(String ID) {
		return baseRep.createBNode(ID);
	}

	public BooleanQuery createBooleanQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createBooleanQuery(ql, query, baseURI);
	}

	public GraphQuery createGraphQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createGraphQuery(ql, query, baseURI);
	}

	public ARTLiteral createLiteral(String literalString, ARTURIResource datatype) {
		return baseRep.createLiteral(literalString, datatype);
	}

	public ARTLiteral createLiteral(String literalString, String language) {
		return baseRep.createLiteral(literalString, language);
	}

	public ARTLiteral createLiteral(String literalString) {
		return baseRep.createLiteral(literalString);
	}

	public Query createQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createQuery(ql, query, baseURI);
	}

	public TupleQuery createTupleQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		return baseRep.createTupleQuery(ql, query, baseURI);
	}

	public ARTURIResource createURIResource(String uri) {
		return baseRep.createURIResource(uri);
	}

	public void deleteStatement(ARTStatement statement, ARTResource... graphs) throws ModelUpdateException {
		baseRep.deleteStatement(statement, graphs);
	}

	public void deleteTriple(ARTResource subject, ARTURIResource property, ARTNode object,
			ARTResource... graphs) throws ModelUpdateException {
		baseRep.deleteTriple(subject, property, object, graphs);
	}

	public String expandQName(String qname) throws ModelAccessException {
		return baseRep.expandQName(qname);
	}

	public String getBaseURI() {
		return baseRep.getBaseURI();
	}

	public String getDefaultNamespace() {
		return baseRep.getDefaultNamespace();
	}

	public Map<String, String> getNamespacePrefixMapping() throws ModelAccessException {
		return baseRep.getNamespacePrefixMapping();
	}

	public String getNSForPrefix(String prefix) throws ModelAccessException {
		return baseRep.getNSForPrefix(prefix);
	}

	public String getPrefixForNS(String namespace) throws ModelAccessException {
		return baseRep.getPrefixForNS(namespace);
	}

	public String getQName(String uri) throws ModelAccessException {
		return baseRep.getQName(uri);
	}

	public boolean hasStatement(ARTStatement stat, boolean inferred, ARTResource... graphs)
			throws ModelAccessException {
		return baseRep.hasStatement(stat, inferred, graphs);
	}

	public boolean hasTriple(ARTResource subj, ARTURIResource pred, ARTNode obj, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		return baseRep.hasTriple(subj, pred, obj, inferred, graphs);
	}

	public ARTResourceIterator listNamedGraphs() throws ModelAccessException {
		return baseRep.listNamedGraphs();
	}

	public ARTNamespaceIterator listNamespaces() throws ModelAccessException {
		return baseRep.listNamespaces();
	}

	public ARTURIResourceIterator listPredicatesOfSubjObjPair(ARTResource subject, ARTNode object,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		return baseRep.listPredicatesOfSubjObjPair(subject, object, inferred, graphs);
	}

	public ARTStatementIterator listStatements(ARTResource subj, ARTURIResource pred, ARTNode obj,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		return baseRep.listStatements(subj, pred, obj, inferred, graphs);
	}

	public ARTResourceIterator listSubjectsOfPredObjPair(ARTURIResource predicate, ARTNode object,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		return baseRep.listSubjectsOfPredObjPair(predicate, object, inferred, graphs);
	}

	public ARTNodeIterator listValuesOfSubjPredPair(ARTResource subject, ARTURIResource predicate,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		return baseRep.listValuesOfSubjPredPair(subject, predicate, inferred, graphs);
	}

	public void removeNsPrefixMapping(String namespace) throws ModelUpdateException {
		baseRep.removeNsPrefixMapping(namespace);
	}

	public void setBaseURI(String uri) throws ModelUpdateException {
		baseRep.setBaseURI(uri);
	}

	public void setDefaultNamespace(String namespace) throws ModelUpdateException {
		baseRep.setDefaultNamespace(namespace);
	}

	public void setNsPrefix(String namespace, String prefix) throws ModelUpdateException {
		baseRep.setNsPrefix(namespace, prefix);
	}

	public void writeRDF(File outputFile, RDFFormat rdfFormat, ARTResource... graphs) throws IOException,
			ModelAccessException, UnsupportedRDFFormatException {
		baseRep.writeRDF(outputFile, rdfFormat, graphs);
	}

	public void writeRDF(OutputStream os, RDFFormat rdfFormat, ARTResource... graphs) throws IOException,
			ModelAccessException, UnsupportedRDFFormatException {
		baseRep.writeRDF(os, rdfFormat, graphs);
	}

}
